﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Lswweb.Common;
using Lswweb.IDAL;
using Lswweb.Model;

namespace Lswweb.Domain
{
    internal class ExpressDeliveryExpressions
    {
        private readonly string expressions;
        private readonly int weight;
        private readonly decimal orderPrice;

        public ExpressDeliveryExpressions(string expressions,int weight,decimal orderPrice)
        {
            this.expressions = expressions.ToLower();
            this.weight = weight;
            this.orderPrice = orderPrice;
        }

        public string Expressions
        {
            get { return this.expressions; }
        }

        public int Weight
        {
            get { return this.weight; }
        }

        public decimal OrderPrice
        {
            get { return this.orderPrice; }
        }

        public decimal CountExpressions()
        {
            if (!IsValid) throw new InvalidCastException("表达式不正确，请检测。");

            MSScriptControl.ScriptControl sc = new MSScriptControl.ScriptControlClass();
            sc.Language = "JavaScript";

            sc.AddCode(this.GetceilScriptCode());
            sc.AddCode(this.GetvalScriptCode());

            var result = sc.Eval(string.Format(
                "Math.floor(eval({0}) * 100 + 0.01) / 100",
                this.ConvertExpressions()));

            return Math.Round(
                result.As<decimal>(),
                2);
        }

        private string GetceilScriptCode()
        {
            StringBuilder scriptBuilder = new StringBuilder();
            scriptBuilder.Append("function getceil(expval) {");
                scriptBuilder.Append("if (eval(expval) > 0) { ");
                    scriptBuilder.Append("return Math.ceil(eval(expval) - 0.000001); ");
                scriptBuilder.Append("} else { ");
                    scriptBuilder.Append("return 0;");
                scriptBuilder.Append("}");
            scriptBuilder.Append("}");

            return scriptBuilder.ToString();
        }

        private string GetvalScriptCode()
        {
            StringBuilder scriptBuilder = new StringBuilder();
            scriptBuilder.Append("function getval(expval) {");
                scriptBuilder.Append("if (eval(expval) > 0.000001) {");
                    scriptBuilder.Append("return 1;");
                scriptBuilder.Append("} else if (eval(expval) > -0.000001 && eval(expval) < 0.000001) {");
                    scriptBuilder.Append("return 1 / 2;");
                scriptBuilder.Append("} else { ");
                    scriptBuilder.Append("return 0;");
                scriptBuilder.Append("}");
            scriptBuilder.Append("}");

            return scriptBuilder.ToString();
        }

        private string ConvertExpressions()
        {
            string str = this.expressions.Replace("[", "getceil(");
            str = str.Replace("]", ")");
            str = str.Replace("{", "getval(");
            str = str.Replace("}", ")");
            str = str.Replace("w", this.Weight.ToString());
            str = str.Replace("p", this.OrderPrice.ToString());

            return str;
        }

        /// <summary>
        /// 验证表达式是否正确
        /// </summary>
        public bool IsValid{

            get { return ValidateExpressions(this.expressions); }
        }

        public static bool ValidateExpressions(string expressions){
            
            return Regex.IsMatch(
                    expressions,
                    @"[.\]\[\}\{\)\(0-9wp\+\-\/\*]+$");
        }
    }

    public class AreaExpressDeliveryExpressions
    {
        private static readonly string EnableOnlinePaymentFieldName = "EnableOnlinePayment";
        private static ITreeTable<CategoryInfo> cityDAL = DALFactory.CreateTreeTableDAL(TreeTableType.Cities);
        private static ILogisticsAccountingDAL logisticsAccountingDAL = DALFactory.CreateLogisticsAccountingDAL();

        public static void Update(int cityId,bool enableOnlinePayment,IList<LogisticsAccountingInfo> logisticsAccountings)
        {
            logisticsAccountings.ForEach(delegate(LogisticsAccountingInfo entity)
            {
                if (!ExpressDeliveryExpressions.ValidateExpressions(entity.Expressions))
                {
                    SiteContext.RenderMessage("错误提示：", "表达式不正确", string.Empty);
                    return;
                }
            });

            try{

                cityDAL.UpdateField(cityId, AreaExpressDeliveryExpressions.EnableOnlinePaymentFieldName, enableOnlinePayment);
                logisticsAccountings.ForEach(delegate(LogisticsAccountingInfo entity)
                {
                    if (logisticsAccountingDAL.IsHas(cityId, entity.LogisticsCompanyId))
                    {
                        logisticsAccountingDAL.Update(entity);
                    }
                    else
                    {
                        logisticsAccountingDAL.Add(entity);
                    }
                });

                SiteContext.RenderMessage(ExceptionType.HandleSuccess);                
            }
            catch(System.Data.DataException){
                SiteContext.RenderMessage(ExceptionType.UnknownError);
            }
        }

        public static decimal CountExpressFee(int cityId,int logisticsCompanyId, int weight, decimal orderPrice)
        {
            LogisticsAccountingInfo logisticsAccounting=AreaExpressDeliveryExpressions.GetLogisticsExpressions(cityId,logisticsCompanyId);

            ExpressDeliveryExpressions expressDeliveryExpressions = new ExpressDeliveryExpressions(
                logisticsAccounting == null ? SiteSettings.Settings.ExpressDeliveryExpressions : logisticsAccounting.Expressions,
                weight,
                orderPrice);

            if (!expressDeliveryExpressions.IsValid){
                throw new InvalidCastException("错误提示：快递运费公式错误，请联系管理员。");
            }

            return expressDeliveryExpressions.CountExpressions();
        }

        public static IList<LogisticsAccountingInfo> GetLogisticsExpressions(int cityId)
        {
            return logisticsAccountingDAL.FindBy(cityId);
        }

        public static LogisticsAccountingInfo GetLogisticsExpressions(int cityId, int logisticsCompanyId)
        {
            return logisticsAccountingDAL.FindBy(cityId, logisticsCompanyId);
        }

        public static bool GetEnableOnlinePayment(int cityId)
        {
            return cityDAL.GetFieldValue(cityId, AreaExpressDeliveryExpressions.EnableOnlinePaymentFieldName).As<bool>();
        }
    }
}
